package Utils

import (
	"math"
)

func GetMaxBox(bytes []byte, width, height int) (int, int, int, int, int, int) {
	fx := width
	fy := height
	tx := 0
	ty := 0
	for i := 0; i < height; i++ {
		for j := 0; j < width; j++ {
			if bytes[i*width+j] != 0 {
				if fx > j {
					fx = j
				}
				if fy > i {
					fy = i
				}
				if tx < j {
					tx = j
				}
				if ty < i {
					ty = i
				}
			}
		}
	}
	return fx, fy, tx, ty, tx - fx, ty - fy
}

// https://www.cnblogs.com/laozhanghahaha/p/12580822.html
var InterpolationMethods = []string{"Nearest Neighbor", "Bilinear", "Bicubic"}

// img 图像
// width height 源图像的宽高
// deg 旋转角度
func RotaGrayImage(img []byte, width, height, deg, maxWidth int, method string) []byte {
	// 计算旋转前的位置 https://zhuanlan.zhihu.com/p/109099445
	var w0 float64 = 0
	var h0 float64 = 0

	halfMaxWidth := maxWidth / 2
	//halfWidth := (maxWidth - width) / 2
	//halfHeight := (maxWidth - height) / 2
	halfWidthImage := width / 2
	halfHeightImage := height / 2

	var degF = float64(deg) / 180 * math.Pi
	// 计算出来的 坐标， int int xy 偏移  int int 源图片大小 []byte 源图像
	var interpolation_method func(float64, float64, int, int, int, int, []byte) (byte, bool)
	if method == InterpolationMethods[0] {
		interpolation_method = interpolation_nearest
	} else if method == InterpolationMethods[1] {
		interpolation_method = interpolation_bilinear
	} else if method == InterpolationMethods[2] {
		interpolation_method = interpolation_bicubic
	}
	copyByteLen := maxWidth * maxWidth
	copyByte := make([]byte, copyByteLen)
	cosV := math.Cos(degF)
	sinV := math.Sin(degF)
	//fmt.Println("change start")
	for w := 0; w < maxWidth; w++ {
		for h := 0; h < maxWidth; h++ {
			w0 = float64(w-halfMaxWidth)*cosV + float64(h-halfMaxWidth)*sinV
			h0 = float64(h-halfMaxWidth)*cosV - float64(w-halfMaxWidth)*sinV
			if b, ok := interpolation_method(w0, h0, halfWidthImage, halfHeightImage, width, height, img); ok {
				copyByte[w+h*maxWidth] = b
			} /* else {
				copyByte[w + h * maxWidth] = 0
			}*/
		}
	}
	//fmt.Println("change over")
	return copyByte
}
func interpolation_nearest(targetW, targetH float64, dX, dY int, width, height int, img []byte) (byte, bool) {
	tw := int(targetW+0.5) + dX
	th := int(targetH+0.5) + dY
	if th < 0 || th >= height || tw < 0 || tw >= width {
		return 0, false
	} else {
		return img[tw+th*width], true
	}
}

// https://blog.csdn.net/ashyash/article/details/116014919
func interpolation_bilinear(targetW, targetH float64, dX, dY int, width, height int, img []byte) (byte, bool) {
	targetW += float64(dX)
	targetH += float64(dY)
	ltx := int(targetW)
	lty := int(targetH)

	if targetH > 0 && targetW > 0 && targetW < float64(width-1) && targetH < float64(height-1) {
		t := math.Abs(targetW - float64(ltx))
		u := math.Abs(targetH - float64(lty))
		a := (1 - t) * (1 - u)
		b := (1 - t) * u
		c := t * u
		d := t * (1 - u)
		return byte(float64(img[ltx+lty*width])*a + float64(img[ltx+lty*width+1])*d + float64(img[ltx+lty*width+width])*b + float64(img[ltx+lty*width+width+1])*c), true
	} else {
		if targetH < 0 || targetH >= float64(height) || targetW < 0 || targetW >= float64(width) {
			return 0, false
		} else {
			if ltx == 0 {
				if targetH == 0 {
					return img[0], true
				} else if targetH >= float64(height) {
					return img[height*width-width+1], true
				} else {
					return (img[lty*width+1] + img[lty*width-width+1]) / 2, true
				}
			} else if lty == 0 {
				return (img[ltx-1] + img[ltx+1]) / 2, true
			} else {
				//fmt.Println(strconv.Itoa(ltx) + "" + strconv.Itoa(lty * width - width + ltx - 1))
				//fmt.Println(strconv.Itoa(lty) + "" + strconv.Itoa(lty * width - width + ltx))
				return (img[lty*width-width+ltx-1] + img[lty*width-width+ltx]) / 2, true
			}
		}
	}
}

// https://www.paulinternet.nl/?page=bicubic
func interpolation_bicubic(targetW, targetH float64, dX, dY int, width, height int, img []byte) (byte, bool) {
	i := int(targetW) + dX
	j := int(targetH) + dY
	u := math.Abs(targetW + float64(dX) - float64(i))
	v := math.Abs(targetH + float64(dY) - float64(j))
	//i += dX
	//j += dY

	if i >= 1 && i <= width-1 && j >= 1 && j <= height-1 {
	} else {
		if i > 0 && i < width && j > 0 && j < height {
			return img[i+j*width], true
		} else {
			return 0, false
		}
	}
	//if i < -3 || i > width + 2 || j < -3 || j > height + 2 {
	//	return 0,false
	//}
	var B = []float64{
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0,
	}
	var loc = [][]int{
		{i - 1, j - 1}, {i, j - 1}, {i + 1, j - 1}, {i + 2, j - 1},
		{i - 1, j}, {i, j}, {i + 1, j}, {i + 2, j},
		{i - 1, j + 1}, {i, j + 1}, {i + 1, j + 1}, {i + 2, j + 1},
		{i - 1, j + 2}, {i, j + 2}, {i + 1, j + 2}, {i + 2, j + 2},
	}
	locInd := 0
	for _, l := range loc {
		if l[0] >= 0 && l[0] < width && l[1] >= 0 && l[1] < height {
			dx := locInd % 4
			dy := locInd / 4
			B[dx+dy*4] = float64(img[l[0]+l[1]*width])
		}
		locInd++
	}
	cbip := func(p []float64, x float64) float64 {
		return p[1] + 0.5*x*(p[2]-p[0]+x*(2.0*p[0]-5.0*p[1]+4.0*p[2]-p[3]+x*(3.0*(p[1]-p[2])+p[3]-p[0])))
	}
	arr := []float64{0, 0, 0, 0}
	arr[0] = cbip(B[0:4], u)
	arr[1] = cbip(B[4:8], u)
	arr[2] = cbip(B[8:12], u)
	arr[3] = cbip(B[12:], u)

	return byte(cbip(arr, v)), true
}

// 下方插值绘制有点怪异，因此暂时弃用
// 三次多项式插值
func interpolation_bicubic_Bspline(x float64) float64 {
	absX := math.Abs(x)
	if absX < 1 {
		return 1 - 2*math.Pow(absX, 2) + math.Pow(absX, 3)
	} else if absX >= 1 && absX < 2 {
		return 4 - 8*absX + 5*math.Pow(absX, 2) - math.Pow(absX, 3)
	} else {
		return 0
	}
}

// https://blog.csdn.net/lz0499/article/details/69791572
// https://www.fatalerrors.org/a/0N530zo.html  双三次(B样条)
func interpolation_bicubic_bspline(targetW, targetH float64, dX, dY int, width, height int, img []byte) (byte, bool) {
	i := int(targetW) // + dX
	j := int(targetH) // + dY
	u := targetW - float64(i)
	v := targetH - float64(j)
	i += dX
	j += dY

	if i < -3 || i > width+2 || j < -3 || j > height+2 {
		return 0, false
	}

	A := []float64{interpolation_bicubic_Bspline(1 + u), interpolation_bicubic_Bspline(u), interpolation_bicubic_Bspline(1 - u), interpolation_bicubic_Bspline(2 - u)}
	C := []float64{interpolation_bicubic_Bspline(1 + v), interpolation_bicubic_Bspline(v), interpolation_bicubic_Bspline(1 - v), interpolation_bicubic_Bspline(2 - v)}
	var B = make([]float64, 16)
	var loc = [][]int{
		{i - 1, j - 1}, {i, j - 1}, {i + 1, j - 1}, {i + 2, j - 1},
		{i - 1, j}, {i, j}, {i + 1, j}, {i + 2, j},
		{i - 1, j + 1}, {i, j + 1}, {i + 1, j + 1}, {i + 2, j + 1},
		{i - 1, j + 2}, {i, j + 2}, {i + 1, j + 2}, {i + 2, j + 2},
	}
	locInd := 0
	for _, l := range loc {
		if l[0] >= 0 && l[0] < width && l[1] >= 0 && l[1] < height {
			B[locInd] = float64(img[l[0]+l[1]*width])
		}
		locInd++
	}

	return byte((A[0]*B[0]+A[1]*B[4]+A[2]*B[8]+A[3]*B[12])*C[0] +
		(A[0]*B[1]+A[1]*B[5]+A[2]*B[9]+A[3]*B[13])*C[1] +
		(A[0]*B[2]+A[1]*B[6]+A[2]*B[10]+A[3]*B[14])*C[2] +
		(A[0]*B[3]+A[1]*B[7]+A[2]*B[11]+A[3]*B[15])*C[3]), true
}
